Police Brutatlity Data Exploration top of page

Contributor(s): Jessica Marx

Date: 12 June 2020

Code: GitHub

Data: Sourced from this Google Sheet and this census data from Kaggle

The following is not meant to be a source of truth in terms of data integrity, but rather to illustrate different ways of displaying, engineering, and/or presenting the data.

# load packages, install if needed
packages = c(
      "dplyr"
    , "ggplot2"
    , "formattable"
    , "plotly"
    , "RColorBrewer"
    , "scales"
    , "stringr"
    , "tidyr"
    , "ElmeR"
    , "RJDBC"
    , "kableExtra"
    , "wesanderson"
    , "reshape2"
    , "rtweet"
    , "tidytext"
    , "lubridate"
    , "wordcloud"
    , "ggpubr"
    , "ggthemes"
    , "knitrBootstrap"
    , "DT"
    , "MatchIt"
    , "beyonce"
    , "UpSetR"
    , "gganimate"
    , "wordcloud2"
    , "widyr"
    , "ggraph"
    , "igraph"
    , "aod"
    , "corrplot"
    , "ROCR"
    , "InformationValue"
    , "car"
    , "glmnet"
    , "caret"
    , "kernlab"
    , "pdp"
    , "rpart.plot"
    , "rpart"
    , "e1071"
    )
package.check <- lapply(packages, FUN = function(x) {
  if (!require(x, character.only = TRUE)) {
    install.packages(x, dependencies = TRUE)
    library(x, character.only = TRUE)
  }
})
#import the data
library(readr)
us_county_stats <- read_csv("datasets_579969_1220276_us_county.csv")
brutality_cases <- read_csv("police_brutality.csv")
# glimpse the data
# us_county_stats %>% 
#   head() 
# 
# brutality_cases %>% 
#   head()

Columns and percent of rows with missing fields from Brutality dataset.

#missing values
colMeans(is.na(brutality_cases))
                                          Timestamp 
                                       1.0000000000 
                       Date of the Police Brutality 
                                       0.0000000000 
           Approximate Time of the Police Brutality 
                                       1.0000000000 
          State where the Police Brutality Occurred 
                                       0.0000000000 
           City where the Police Brutality Occurred 
                                       0.0007829832 
                  Video URL of the Police Brutality 
                                       0.4519117839 
                          Names of Victims Involved 
                                       0.0000000000 
                  Names of Police Officers Involved 
                                       1.0000000000 
          Badge Numbers of Police Officers involved 
                                       1.0000000000 
Enter any extra information you want to share here. 
                                       1.0000000000 
                                           Category 
                                       1.0000000000 
                                               test 
                                       1.0000000000 
                                             County 
                                       0.0000000000 
                                         Category_1 
                                       1.0000000000 

Summary of US census dataset.

summary(us_county_stats)
      fips          county             state          
 Min.   : 1001   Length:3220        Length:3220       
 1st Qu.:19032   Class :character   Class :character  
 Median :30024   Mode  :character   Mode  :character  
 Mean   :31394                                        
 3rd Qu.:46106                                        
 Max.   :72153                                        
  state_code             male             female       
 Length:3220        Min.   :     38   Min.   :     37  
 Class :character   1st Qu.:   5658   1st Qu.:   5573  
 Mode  :character   Median :  12916   Median :  12996  
                    Mean   :  49875   Mean   :  51457  
                    3rd Qu.:  33248   3rd Qu.:  33531  
                    Max.   :4976788   Max.   :5121264  
   median_age      population       female_percentage
 Min.   :21.70   Min.   :      75   Min.   :21.00    
 1st Qu.:38.10   1st Qu.:   11214   1st Qu.:49.43    
 Median :41.20   Median :   25950   Median :50.42    
 Mean   :41.28   Mean   :  101332   Mean   :49.96    
 3rd Qu.:44.30   3rd Qu.:   66552   3rd Qu.:51.15    
 Max.   :67.00   Max.   :10098052   Max.   :58.61    
      lat             long        
 Min.   :17.98   Min.   :-164.03  
 1st Qu.:34.35   1st Qu.: -98.09  
 Median :38.21   Median : -89.95  
 Mean   :37.97   Mean   : -91.64  
 3rd Qu.:41.69   3rd Qu.: -82.99  
 Max.   :69.31   Max.   : -65.29  
# Clean dataset (drop nulls)
brutality_clean = brutality_cases
# Remove columns with more than 50% NA
brutality_clean = brutality_clean[, which(colMeans(!is.na(brutality_clean)) > 0.5)]
# Change columns to snake case
snake_case <- function(x) {
  colnames(x) <- gsub(" ", "_", colnames(x));x
  colnames(x) <-tolower(colnames(x))
}
colnames(brutality_clean) = snake_case(brutality_clean)
brutality_clean = brutality_clean %>% 
  rename(
   "state" = state_where_the_police_brutality_occurred
   , "city" = city_where_the_police_brutality_occurred
   , "date" = date_of_the_police_brutality
  ) %>% 
  mutate(
    state_county = paste0(state, "-", county)
  )
brutality_clean$date = as.Date(brutality_clean$date, "%m/%d/%Y")
us_stats_clean = us_county_stats
us_stats_clean$county = str_replace(us_stats_clean$county, " County", "")
us_stats_clean = us_stats_clean %>% 
  group_by(state) %>% 
  mutate(state_pop = sum(population)) %>% 
  rename(
    "state_full" = state
    , "state" = state_code
    ) %>% 
  ungroup() 
# us_stats_clean = 
  
us_stats_state = us_stats_clean %>% 
  mutate(state_county = paste0(state, "-", county)) %>% 
  select(state_pop, state, state_full) %>% 
  unique()
df_merged = brutality_clean %>% 
  left_join(us_stats_state) %>% 
  mutate(state_pop = ifelse(state == "DC", 705749, state_pop)
  ) %>% 
  unique()
df_merged = df_merged %>% 
  group_by(
    state
  ) %>% 
  add_tally() %>% 
  ungroup() %>% 
  rename(
    "state_totals" = n
  ) %>% 
  mutate(
    state_pop_millions = state_pop/1000000
    , totals_per_million = state_totals/state_pop_millions
  ) 

Incidents by State

df_plot = df_merged %>% 
  select(
    state, state_pop, state_full, state_pop_millions, state_totals, totals_per_million
  ) %>% 
  unique() %>% 
  arrange(desc(totals_per_million)) %>% 
  mutate(
    state = reorder(as.factor(state), totals_per_million)
  ) 
a = df_plot %>% 
  plot_ly(
    y = ~state
    , x = ~totals_per_million
    , type = "bar"
    , marker = list(color = "#35A7FF")
    , orientation = "h"
    , width = 900
    , height = 1000
    , hoverinfo = "text"
    , text = ~paste(
      "State: ", state_full
      , "<br>State Population: ", comma(state_pop)
      , "<br>Reported Incidents: ", state_totals
      , "<br>Incidents per Million Residents: ", comma(totals_per_million)
    )
  ) %>% 
  layout(
    xaxis = list(title = "Reported Incidents per Million State Residents")
    , yaxis = list(title = "")
  )
b = df_plot %>% 
  plot_ly(
    y = ~state
    , x = ~state_totals
    , type = "bar"
    , marker = list(color = "#35A7FF")
    , orientation = "h"
    , width = 900
    , height = 1000
    , hoverinfo = "text"
    , showlegend = FALSE
    , text = ~paste(
      "State: ", state_full
      , "<br>State Population: ", comma(state_pop)
      , "<br>Reported Incidents: ", state_totals
      , "<br>Incidents per Million Residents: ", comma(totals_per_million)
    )
  ) %>% 
  layout(
    xaxis = list(title = "Total Reported Incidents")
    , yaxis = list(title = "")
  )
subplot(a, b, titleX = TRUE)

Excluding counties with null populations

There are some counties with null values when it comes to population. We can obviously find these via a better dataset, but for now I’m going to exclude them in order to provide examples with the data that we have. Obviously, this is not accurate reporting.

County Incidents per Capita

df_filtered = df_merged
df_filtered = df_filtered %>% 
  left_join(us_stats_clean) %>% 
  filter(!is.na(population)) 
df_filtered = df_filtered %>% 
  group_by(state, county) %>% 
  add_tally() %>% 
  rename("county_totals" = n) %>% 
  arrange(desc(county_totals)) %>% 
  mutate("incidents_per_capita" = county_totals/(population)) %>% 
  ungroup() 
df_county = df_filtered %>% 
  select(state, state_county, county, state_full, state_totals, median_age, population, county_totals, incidents_per_capita) %>% 
  unique() %>% 
  arrange(desc(incidents_per_capita))
df_county %>% 
  mutate(
    incidents_per_capita = comma(incidents_per_capita, .000001)
    , population = comma(population)
    ) %>% 
  datatable()
LS0tCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0b2M6IGZhbHNlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogNQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgCi0tLQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoKYm9keSB7CiAgZm9udC1zaXplOiAxMnB0OwogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwp9Cgp0aCB7CiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMzVBN0ZGOwogICAgY29sb3I6IGJsYWNrOwogICAgZm9udC1zaXplOiAxMHB0OwogICAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIHNhbnMtc2VyaWY7CiAgICB0ZXh0LWFsaWduOiBsZWZ0OwogICAgPCEtLSBtYXJnaW4tbGVmdDogYXV0bzsgLS0+CiAgICA8IS0tIG1hcmdpbi1yaWdodDogYXV0bzsgLS0+CiAgICA8IS0tIHBhZGRpbmctdG9wOiAyNXB4OyAtLT4KICB9Cgp0ZCB7ICAvKiBUYWJsZSAgKi8KICBmb250LXNpemU6IDEwcHQ7CiAgPCEtLSB0ZXh0LWFsaWduOiBjZW50ZXI7IC0tPgogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwogIDwhLS0gcGFkZGluZy10b3A6IDI1cHg7IC0tPgp9CgpoMSB7CiAgZm9udC1zaXplOiAxNnB0OwogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwp9CiAgCmgyIHsKICBmb250LXNpemU6IDEycHQ7CiAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIHNhbnMtc2VyaWY7CiAgY29sb3I6ICMxZjc4YjQ7CiAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIHNhbnMtc2VyaWY7Cn0KCmgzIHsKICBmb250LXNpemU6IDEycHQ7CiAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIHNhbnMtc2VyaWY7CiAgfQogIApoNCB7CiAgZm9udC1zaXplOiAxMnB0OwogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwp9Cmg1IHsKICBmb250LXNpemU6IDEycHQ7CiAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIHNhbnMtc2VyaWY7Cn0KYSB7CiAgY29sb3I6ICMzNUE3RkY7CiAgZm9udC1zaXplOiAxMnB0OwogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwp9CgoKLnNpZGVuYXYgewogIGhlaWdodDogMTAwJTsKICB3aWR0aDogMjAwcHg7CiAgcG9zaXRpb246IGZpeGVkOwogIHotaW5kZXg6IDE7CiAgdG9wOiAwOwogIGxlZnQ6IDA7CiAgYmFja2dyb3VuZC1jb2xvcjogIzM4NjE4QzsKICBvdmVyZmxvdy14OiBoaWRkZW47CiAgcGFkZGluZy10b3A6IDIwcHgKICBwYWRkaW5nLWxlZnQ6IDIwcHg7Cn0KCi5zaWRlbmF2IGEgewogIHBhZGRpbmc6IDZweCA4cHggNnB4IDE2cHg7CiAgdGV4dC1kZWNvcmF0aW9uOiBub25lOwogIGZvbnQtc2l6ZTogMTZwdDsKICBmb250LXdlaWdodDogYm9sZGVyOwogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwogIGNvbG9yOiAjRkZGRkZGOwogIGRpc3BsYXk6IGJsb2NrOwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQoKLmNlbnRlciB7CiAgZGlzcGxheTogYmxvY2s7CiAgbWFyZ2luLWxlZnQ6IGF1dG87CiAgbWFyZ2luLXJpZ2h0OiBhdXRvOwogIHdpZHRoOiAxMDAlOwp9Cgouc2lkZW5hdiBhOmhvdmVyIHsKICBjb2xvcjogI2YxZjFmMTsKfQoKLm1haW4gewogIG1hcmdpbi1sZWZ0OiAyMDBweDsgLyogU2FtZSBhcyB0aGUgd2lkdGggb2YgdGhlIHNpZGVuYXYgKi8KCn0KLm1haW4tY29udGFpbmVyIHsKICBtYXgtd2lkdGg6IDE0MDBweDsKICBtYXJnaW4tbGVmdDogYXV0bzsKICBtYXJnaW4tcmlnaHQ6IGF1dG87CiAgcGFkZGluZzogMjVweAp9CiAgLypwYWRkaW5nOiAwcHggNXB4OyAqLwp9CgpAbWVkaWEgc2NyZWVuIGFuZCAobWF4LWhlaWdodDogNDUwcHgpIHsKICAuc2lkZW5hdiB7cGFkZGluZy10b3A6IDE1cHg7fQogIC5zaWRlbmF2IGEge2ZvbnQtc2l6ZTogMThweDt9ICAKfQo8L3N0eWxlPgoKPCEtLSBUSVRMRSBJTkZPICAtLT4KCjxkaXYgY2xhc3M9InNpZGVuYXYiPgogIDxpbWcgc3JjPSJncmFzc3Jvb3RzX2xhdy5wbmciIGFsdD0iIiB3aWR0aD0xMDBweCBjbGFzcz0iY2VudGVyIi8+CiAgPGEgaHJlZj0iI2Fib3V0Ij5Qb2xpY2UgQnJ1dGF0bGl0eSBEYXRhIEV4cGxvcmF0aW9uPC9hPgogIDxhIGhyZWY9IiN0b3AiPiA8Zm9udCBmYWNlPSJBcmlhbCIgc2l6ZT0iMiIgY29sb3I9ICIjMzVBN0ZGIj4gdG9wIG9mIHBhZ2UgPC9mb250PjwvYT4KPC9kaXY+Cgo8IS0tIENPTlRFTlQgU1RBUlRTIEhFUkUgIC0tPgoKPGRpdiBjbGFzcz0ibWFpbiI+CjxkaXYgY2xhc3M9ImJvZHkiPgoKIyMgX19Db250cmlidXRvcihzKTpfXyBKZXNzaWNhIE1hcngKIyMgX19EYXRlOl9fIGByIGZvcm1hdChTeXMudGltZSgpLCAiJWQgJUIgJVkiKWAKIyMgX19Db2RlOl9fIDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9temFnYWlub3ZhL3BiLWRhc2hib2FyZC90cmVlL21hc3Rlci93aWRnZXRzIiB0YXJnZXQ9Il9ibGFuayI+R2l0SHViPC9hPgojIyA8Yj5EYXRhOjwvYj4gU291cmNlZCBmcm9tIHRoaXMgPGEgaHJlZj0iaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMTF3TFdNOTU3d2t4Ukl5SzdFRlctU285anFHaHZReGNaODNpcG0xa1ZFblEvZWRpdCNnaWQ9OTE2NDExODA2IiB0YXJnZXQ9Il9ibGFuayI+R29vZ2xlIFNoZWV0PC9hPiBhbmQgdGhpcyBjZW5zdXMgZGF0YSBmcm9tIDxhIGhyZWY9Imh0dHBzOi8vd3d3LmthZ2dsZS5jb20vaGVhZHNvcnRhaWxzL2NvdmlkMTktdXMtY291bnR5LWpodS1kYXRhLWRlbW9ncmFwaGljcz9zZWxlY3Q9dXNfY291bnR5LmNzdiIgdGFyZ2V0PSJfYmxhbmsiPkthZ2dsZTwvYT4KClRoZSBmb2xsb3dpbmcgaXMgbm90IG1lYW50IHRvIGJlIGEgc291cmNlIG9mIHRydXRoIGluIHRlcm1zIG9mIGRhdGEgaW50ZWdyaXR5LCBidXQgcmF0aGVyIHRvIGlsbHVzdHJhdGUgZGlmZmVyZW50IHdheXMgb2YgZGlzcGxheWluZywgZW5naW5lZXJpbmcsIGFuZC9vciBwcmVzZW50aW5nIHRoZSBkYXRhLiAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSkKCmBgYAoKCmBgYHtyIHBhY2thZ2UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIGxvYWQgcGFja2FnZXMsIGluc3RhbGwgaWYgbmVlZGVkCnBhY2thZ2VzID0gYygKICAgICAgImRwbHlyIgogICAgLCAiZ2dwbG90MiIKICAgICwgImZvcm1hdHRhYmxlIgogICAgLCAicGxvdGx5IgogICAgLCAiUkNvbG9yQnJld2VyIgogICAgLCAic2NhbGVzIgogICAgLCAic3RyaW5nciIKICAgICwgInRpZHlyIgogICAgLCAiRWxtZVIiCiAgICAsICJSSkRCQyIKICAgICwgImthYmxlRXh0cmEiCiAgICAsICJ3ZXNhbmRlcnNvbiIKICAgICwgInJlc2hhcGUyIgogICAgLCAicnR3ZWV0IgogICAgLCAidGlkeXRleHQiCiAgICAsICJsdWJyaWRhdGUiCiAgICAsICJ3b3JkY2xvdWQiCiAgICAsICJnZ3B1YnIiCiAgICAsICJnZ3RoZW1lcyIKICAgICwgImtuaXRyQm9vdHN0cmFwIgogICAgLCAiRFQiCiAgICAsICJNYXRjaEl0IgogICAgLCAiYmV5b25jZSIKICAgICwgIlVwU2V0UiIKICAgICwgImdnYW5pbWF0ZSIKICAgICwgIndvcmRjbG91ZDIiCiAgICAsICJ3aWR5ciIKICAgICwgImdncmFwaCIKICAgICwgImlncmFwaCIKICAgICwgImFvZCIKICAgICwgImNvcnJwbG90IgogICAgLCAiUk9DUiIKICAgICwgIkluZm9ybWF0aW9uVmFsdWUiCiAgICAsICJjYXIiCiAgICAsICJnbG1uZXQiCiAgICAsICJjYXJldCIKICAgICwgImtlcm5sYWIiCiAgICAsICJwZHAiCiAgICAsICJycGFydC5wbG90IgogICAgLCAicnBhcnQiCiAgICAsICJlMTA3MSIKICAgICkKCnBhY2thZ2UuY2hlY2sgPC0gbGFwcGx5KHBhY2thZ2VzLCBGVU4gPSBmdW5jdGlvbih4KSB7CiAgaWYgKCFyZXF1aXJlKHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKICAgIGluc3RhbGwucGFja2FnZXMoeCwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICAgIGxpYnJhcnkoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQogIH0KfSkKYGBgCgoKYGBge3IgZnVuY3Rpb25zLCBlY2hvPUZBTFNFfQoKI2Z1bmN0aW9ucyEKCiNyb3VuZCAKcm91bmQudG8gPC0gZnVuY3Rpb24oeCwgYikgewogIHJvdW5kKHgvYikqYgp9Cgojb2RkcyB0byBwcm9iYWJpbGl0eQpvZGRzLnRvLnByb2IgPC0gZnVuY3Rpb24ob2RkcykgewogIG9kZHMvKDEgKyBvZGRzKSAKfQoKI2xvZyBvZGRzIHRvIHByb2JhYmlsaXR5IApsb2dpdDJwcm9iIDwtIGZ1bmN0aW9uKGxvZ2l0KXsKICBvZGRzIDwtIGV4cChsb2dpdCkKICBwcm9iIDwtIG9kZHMgLyAoMSArIG9kZHMpCiAgcmV0dXJuKHByb2IpCn0KCiNjb252ZXJ0IHRvIGEgcmFuZ2UKcmFuZ2UwMSA8LSBmdW5jdGlvbih4KXsKICAoeC1taW4oeCkpLyhtYXgoeCktbWluKHgpKQp9CgojZnVuY3Rpb24gdG8gZ2V0IHZlY3RvciBvZiBjb2xvciB2YWx1ZXMgZnJvbSBSQ29sb3JCcmV3ZXIKZ2V0X2hleF92YWx1ZXMgPC0gZnVuY3Rpb24ocGFsKSB7CiAgYnJld2VyLnBhbChicmV3ZXIucGFsLmluZm9bcGFsLCAibWF4Y29sb3JzIl0sIHBhbCkKfQpwYWlyZWRfY29scyA8LSBnZXRfaGV4X3ZhbHVlcyhwYWwgPSAiUGFpcmVkIikKCiNwYWlyZWQgcGFsZXR0ZSB3aXRoIGJyaWdodGVyIHllbGxvdyAodXNlIHRoaXMgZm9yIGRpdmlzaW9uYWwgY29sb3IgbWFwcGluZykKcGFpcmVkX2JldHRlciA8LSByZXBsYWNlKHBhaXJlZF9jb2xzLCBwYWlyZWRfY29scyA9PSAiI0ZGRkY5OSIsICIjZmVkOTc2IikKCiNyb3VuZCB0byBuZWFyZXN0IDUsIDEwLCBldGMgCnJvdW5kX25lYXJlc3QgPSBmdW5jdGlvbih4LCBiYXNlKSB7CiAgYmFzZSpyb3VuZCh4L2Jhc2UpCn0KCmxlZnQgPSBmdW5jdGlvbih0ZXh0LCBudW1fY2hhcikgewogIHN1YnN0cih0ZXh0LCAxLCBudW1fY2hhcikKfQoKbWlkID0gZnVuY3Rpb24odGV4dCwgc3RhcnRfbnVtLCBudW1fY2hhcikgewogIHN1YnN0cih0ZXh0LCBzdGFydF9udW0sIHN0YXJ0X251bSArIG51bV9jaGFyIC0gMSkKfQoKcmlnaHQgPSBmdW5jdGlvbih0ZXh0LCBudW1fY2hhcikgewogIHN1YnN0cih0ZXh0LCBuY2hhcih0ZXh0KSAtIChudW1fY2hhci0xKSwgbmNoYXIodGV4dCkpCn0KCiN0dXJuIHZhbHVlcyBvdXRzaWRlIG9mIElRUiB0byBOQQpvdXRsaWVycmVwbGFjZW1lbnQgPC0gZnVuY3Rpb24oZGF0YWZyYW1lKXsKICBkYXRhZnJhbWUgJT4lICAgICAgICAgIAogICAgICAgICAgIG1hcF9pZihpcy5udW1lcmljLCB+IHJlcGxhY2UoLngsIC54ICVpbiUgYm94cGxvdC5zdGF0cygueCkkb3V0LCBOQSkpICU+JQogICAgICAgICAgIGRwbHlyOjpiaW5kX2NvbHMoKSAKfQoKI2V2YWx1YXRlIG1vZGVsCmV2YWxfbWV0cmljcyA9IGZ1bmN0aW9uKG1vZGVsLCBkZiwgcHJlZGljdGlvbnMsIHRhcmdldCl7CiAgcmVzaWRzID0gZGZbLHRhcmdldF0gLSBwcmVkaWN0aW9ucwogIHJlc2lkczIgPSByZXNpZHMqKjIKICBOID0gbGVuZ3RoKHByZWRpY3Rpb25zKQogIHIyID0gYXMuY2hhcmFjdGVyKHJvdW5kKHN1bW1hcnkobW9kZWwpJHIuc3F1YXJlZCwgMikpCiAgYWRqX3IyID0gYXMuY2hhcmFjdGVyKHJvdW5kKHN1bW1hcnkobW9kZWwpJGFkai5yLnNxdWFyZWQsIDIpKQogIHByaW50KGFkal9yMikgI0FkanVzdGVkIFItc3F1YXJlZAogIHByaW50KGFzLmNoYXJhY3Rlcihyb3VuZChzcXJ0KHN1bShyZXNpZHMyKS9OKSwgMikpKSAjUk1TRQp9CgpgYGAKCmBgYHtyfQoKI2ltcG9ydCB0aGUgZGF0YQpsaWJyYXJ5KHJlYWRyKQp1c19jb3VudHlfc3RhdHMgPC0gcmVhZF9jc3YoImRhdGFzZXRzXzU3OTk2OV8xMjIwMjc2X3VzX2NvdW50eS5jc3YiKQpicnV0YWxpdHlfY2FzZXMgPC0gcmVhZF9jc3YoInBvbGljZV9icnV0YWxpdHkuY3N2IikKCmBgYAoKCmBgYHtyfQoKIyBnbGltcHNlIHRoZSBkYXRhCiMgdXNfY291bnR5X3N0YXRzICU+JSAKIyAgIGhlYWQoKSAKIyAKIyBicnV0YWxpdHlfY2FzZXMgJT4lIAojICAgaGVhZCgpCgpgYGAKCiMjIENvbHVtbnMgYW5kIHBlcmNlbnQgb2Ygcm93cyB3aXRoIG1pc3NpbmcgZmllbGRzIGZyb20gQnJ1dGFsaXR5IGRhdGFzZXQuIApgYGB7cn0KCiNtaXNzaW5nIHZhbHVlcwpjb2xNZWFucyhpcy5uYShicnV0YWxpdHlfY2FzZXMpKQoKYGBgCgojIyBTdW1tYXJ5IG9mIFVTIGNlbnN1cyBkYXRhc2V0LiAKCmBgYHtyfQoKc3VtbWFyeSh1c19jb3VudHlfc3RhdHMpCgpgYGAKCmBgYHtyfQoKIyBDbGVhbiBkYXRhc2V0IChkcm9wIG51bGxzKQpicnV0YWxpdHlfY2xlYW4gPSBicnV0YWxpdHlfY2FzZXMKCiMgUmVtb3ZlIGNvbHVtbnMgd2l0aCBtb3JlIHRoYW4gNTAlIE5BCmJydXRhbGl0eV9jbGVhbiA9IGJydXRhbGl0eV9jbGVhblssIHdoaWNoKGNvbE1lYW5zKCFpcy5uYShicnV0YWxpdHlfY2xlYW4pKSA+IDAuNSldCgojIENoYW5nZSBjb2x1bW5zIHRvIHNuYWtlIGNhc2UKCnNuYWtlX2Nhc2UgPC0gZnVuY3Rpb24oeCkgewogIGNvbG5hbWVzKHgpIDwtIGdzdWIoIiAiLCAiXyIsIGNvbG5hbWVzKHgpKTt4CiAgY29sbmFtZXMoeCkgPC10b2xvd2VyKGNvbG5hbWVzKHgpKQp9Cgpjb2xuYW1lcyhicnV0YWxpdHlfY2xlYW4pID0gc25ha2VfY2FzZShicnV0YWxpdHlfY2xlYW4pCgpicnV0YWxpdHlfY2xlYW4gPSBicnV0YWxpdHlfY2xlYW4gJT4lIAogIHJlbmFtZSgKICAgInN0YXRlIiA9IHN0YXRlX3doZXJlX3RoZV9wb2xpY2VfYnJ1dGFsaXR5X29jY3VycmVkCiAgICwgImNpdHkiID0gY2l0eV93aGVyZV90aGVfcG9saWNlX2JydXRhbGl0eV9vY2N1cnJlZAogICAsICJkYXRlIiA9IGRhdGVfb2ZfdGhlX3BvbGljZV9icnV0YWxpdHkKICApICU+JSAKICBtdXRhdGUoCiAgICBzdGF0ZV9jb3VudHkgPSBwYXN0ZTAoc3RhdGUsICItIiwgY291bnR5KQogICkKCmJydXRhbGl0eV9jbGVhbiRkYXRlID0gYXMuRGF0ZShicnV0YWxpdHlfY2xlYW4kZGF0ZSwgIiVtLyVkLyVZIikKCmBgYAoKYGBge3J9Cgp1c19zdGF0c19jbGVhbiA9IHVzX2NvdW50eV9zdGF0cwoKdXNfc3RhdHNfY2xlYW4kY291bnR5ID0gc3RyX3JlcGxhY2UodXNfc3RhdHNfY2xlYW4kY291bnR5LCAiIENvdW50eSIsICIiKQoKdXNfc3RhdHNfY2xlYW4gPSB1c19zdGF0c19jbGVhbiAlPiUgCiAgZ3JvdXBfYnkoc3RhdGUpICU+JSAKICBtdXRhdGUoc3RhdGVfcG9wID0gc3VtKHBvcHVsYXRpb24pKSAlPiUgCiAgcmVuYW1lKAogICAgInN0YXRlX2Z1bGwiID0gc3RhdGUKICAgICwgInN0YXRlIiA9IHN0YXRlX2NvZGUKICAgICkgJT4lIAogIHVuZ3JvdXAoKSAKCiMgdXNfc3RhdHNfY2xlYW4gPSAKICAKdXNfc3RhdHNfc3RhdGUgPSB1c19zdGF0c19jbGVhbiAlPiUgCiAgbXV0YXRlKHN0YXRlX2NvdW50eSA9IHBhc3RlMChzdGF0ZSwgIi0iLCBjb3VudHkpKSAlPiUgCiAgc2VsZWN0KHN0YXRlX3BvcCwgc3RhdGUsIHN0YXRlX2Z1bGwpICU+JSAKICB1bmlxdWUoKQoKYGBgCgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CgpkZl9tZXJnZWQgPSBicnV0YWxpdHlfY2xlYW4gJT4lIAogIGxlZnRfam9pbih1c19zdGF0c19zdGF0ZSkgJT4lIAogIG11dGF0ZShzdGF0ZV9wb3AgPSBpZmVsc2Uoc3RhdGUgPT0gIkRDIiwgNzA1NzQ5LCBzdGF0ZV9wb3ApCiAgKSAlPiUgCiAgdW5pcXVlKCkKCmRmX21lcmdlZCA9IGRmX21lcmdlZCAlPiUgCiAgZ3JvdXBfYnkoCiAgICBzdGF0ZQogICkgJT4lIAogIGFkZF90YWxseSgpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIHJlbmFtZSgKICAgICJzdGF0ZV90b3RhbHMiID0gbgogICkgJT4lIAogIG11dGF0ZSgKICAgIHN0YXRlX3BvcF9taWxsaW9ucyA9IHN0YXRlX3BvcC8xMDAwMDAwCiAgICAsIHRvdGFsc19wZXJfbWlsbGlvbiA9IHN0YXRlX3RvdGFscy9zdGF0ZV9wb3BfbWlsbGlvbnMKICApIAoKYGBgCgojIyBJbmNpZGVudHMgYnkgU3RhdGUKCmBgYHtyfQoKZGZfcGxvdCA9IGRmX21lcmdlZCAlPiUgCiAgc2VsZWN0KAogICAgc3RhdGUsIHN0YXRlX3BvcCwgc3RhdGVfZnVsbCwgc3RhdGVfcG9wX21pbGxpb25zLCBzdGF0ZV90b3RhbHMsIHRvdGFsc19wZXJfbWlsbGlvbgogICkgJT4lIAogIHVuaXF1ZSgpICU+JSAKICBhcnJhbmdlKGRlc2ModG90YWxzX3Blcl9taWxsaW9uKSkgJT4lIAogIG11dGF0ZSgKICAgIHN0YXRlID0gcmVvcmRlcihhcy5mYWN0b3Ioc3RhdGUpLCB0b3RhbHNfcGVyX21pbGxpb24pCiAgKSAKCmEgPSBkZl9wbG90ICU+JSAKICBwbG90X2x5KAogICAgeSA9IH5zdGF0ZQogICAgLCB4ID0gfnRvdGFsc19wZXJfbWlsbGlvbgogICAgLCB0eXBlID0gImJhciIKICAgICwgbWFya2VyID0gbGlzdChjb2xvciA9ICIjMzVBN0ZGIikKICAgICwgb3JpZW50YXRpb24gPSAiaCIKICAgICwgd2lkdGggPSA5MDAKICAgICwgaGVpZ2h0ID0gMTAwMAogICAgLCBob3ZlcmluZm8gPSAidGV4dCIKICAgICwgdGV4dCA9IH5wYXN0ZSgKICAgICAgIlN0YXRlOiAiLCBzdGF0ZV9mdWxsCiAgICAgICwgIjxicj5TdGF0ZSBQb3B1bGF0aW9uOiAiLCBjb21tYShzdGF0ZV9wb3ApCiAgICAgICwgIjxicj5SZXBvcnRlZCBJbmNpZGVudHM6ICIsIHN0YXRlX3RvdGFscwogICAgICAsICI8YnI+SW5jaWRlbnRzIHBlciBNaWxsaW9uIFJlc2lkZW50czogIiwgY29tbWEodG90YWxzX3Blcl9taWxsaW9uKQogICAgKQogICkgJT4lIAogIGxheW91dCgKICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJSZXBvcnRlZCBJbmNpZGVudHMgcGVyIE1pbGxpb24gU3RhdGUgUmVzaWRlbnRzIikKICAgICwgeWF4aXMgPSBsaXN0KHRpdGxlID0gIiIpCiAgKQoKYiA9IGRmX3Bsb3QgJT4lIAogIHBsb3RfbHkoCiAgICB5ID0gfnN0YXRlCiAgICAsIHggPSB+c3RhdGVfdG90YWxzCiAgICAsIHR5cGUgPSAiYmFyIgogICAgLCBtYXJrZXIgPSBsaXN0KGNvbG9yID0gIiMzNUE3RkYiKQogICAgLCBvcmllbnRhdGlvbiA9ICJoIgogICAgLCB3aWR0aCA9IDkwMAogICAgLCBoZWlnaHQgPSAxMDAwCiAgICAsIGhvdmVyaW5mbyA9ICJ0ZXh0IgogICAgLCBzaG93bGVnZW5kID0gRkFMU0UKICAgICwgdGV4dCA9IH5wYXN0ZSgKICAgICAgIlN0YXRlOiAiLCBzdGF0ZV9mdWxsCiAgICAgICwgIjxicj5TdGF0ZSBQb3B1bGF0aW9uOiAiLCBjb21tYShzdGF0ZV9wb3ApCiAgICAgICwgIjxicj5SZXBvcnRlZCBJbmNpZGVudHM6ICIsIHN0YXRlX3RvdGFscwogICAgICAsICI8YnI+SW5jaWRlbnRzIHBlciBNaWxsaW9uIFJlc2lkZW50czogIiwgY29tbWEodG90YWxzX3Blcl9taWxsaW9uKQogICAgKQogICkgJT4lIAogIGxheW91dCgKICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJUb3RhbCBSZXBvcnRlZCBJbmNpZGVudHMiKQogICAgLCB5YXhpcyA9IGxpc3QodGl0bGUgPSAiIikKICApCgpzdWJwbG90KGEsIGIsIHRpdGxlWCA9IFRSVUUpCgpgYGAKCiMjIyBfRXhjbHVkaW5nIGNvdW50aWVzIHdpdGggbnVsbCBwb3B1bGF0aW9uc18KVGhlcmUgYXJlIHNvbWUgY291bnRpZXMgd2l0aCBudWxsIHZhbHVlcyB3aGVuIGl0IGNvbWVzIHRvIHBvcHVsYXRpb24uIFdlIGNhbiBvYnZpb3VzbHkgZmluZCB0aGVzZSB2aWEgYSBiZXR0ZXIgZGF0YXNldCwgYnV0IGZvciBub3cgSSdtIGdvaW5nIHRvIGV4Y2x1ZGUgdGhlbSBpbiBvcmRlciB0byBwcm92aWRlIGV4YW1wbGVzIHdpdGggdGhlIGRhdGEgdGhhdCB3ZSBoYXZlLiBfT2J2aW91c2x5LCB0aGlzIGlzIG5vdCBhY2N1cmF0ZSByZXBvcnRpbmcuXwoKIyMgQ291bnR5IEluY2lkZW50cyBwZXIgQ2FwaXRhIAoKYGBge3J9CgpkZl9maWx0ZXJlZCA9IGRmX21lcmdlZAoKZGZfZmlsdGVyZWQgPSBkZl9maWx0ZXJlZCAlPiUgCiAgbGVmdF9qb2luKHVzX3N0YXRzX2NsZWFuKSAlPiUgCiAgZmlsdGVyKCFpcy5uYShwb3B1bGF0aW9uKSkgCgpkZl9maWx0ZXJlZCA9IGRmX2ZpbHRlcmVkICU+JSAKICBncm91cF9ieShzdGF0ZSwgY291bnR5KSAlPiUgCiAgYWRkX3RhbGx5KCkgJT4lIAogIHJlbmFtZSgiY291bnR5X3RvdGFscyIgPSBuKSAlPiUgCiAgYXJyYW5nZShkZXNjKGNvdW50eV90b3RhbHMpKSAlPiUgCiAgbXV0YXRlKCJpbmNpZGVudHNfcGVyX2NhcGl0YSIgPSBjb3VudHlfdG90YWxzLyhwb3B1bGF0aW9uKSkgJT4lIAogIHVuZ3JvdXAoKSAKCmRmX2NvdW50eSA9IGRmX2ZpbHRlcmVkICU+JSAKICBzZWxlY3Qoc3RhdGUsIHN0YXRlX2NvdW50eSwgY291bnR5LCBzdGF0ZV9mdWxsLCBzdGF0ZV90b3RhbHMsIG1lZGlhbl9hZ2UsIHBvcHVsYXRpb24sIGNvdW50eV90b3RhbHMsIGluY2lkZW50c19wZXJfY2FwaXRhKSAlPiUgCiAgdW5pcXVlKCkgJT4lIAogIGFycmFuZ2UoZGVzYyhpbmNpZGVudHNfcGVyX2NhcGl0YSkpCgpkZl9jb3VudHkgJT4lIAogIG11dGF0ZSgKICAgIGluY2lkZW50c19wZXJfY2FwaXRhID0gY29tbWEoaW5jaWRlbnRzX3Blcl9jYXBpdGEsIC4wMDAwMDEpCiAgICAsIHBvcHVsYXRpb24gPSBjb21tYShwb3B1bGF0aW9uKQogICAgKSAlPiUgCiAgZGF0YXRhYmxlKCkKCgpgYGAKCg==